﻿using System;
using System.Configuration;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Web;

namespace Nasa8x.Web.UI.Handlers
{

    public class ResourceHandler : IHttpHandler
    {

        
        private readonly static TimeSpan CACHE_DURATION = TimeSpan.FromDays(30);

        public void ProcessRequest(HttpContext context)
        {

            HttpRequest _request = context.Request;

            // Read setName, contentType and version. All are required. They are
            // used as cache key
            string _setName = _request["k"] ?? string.Empty;
            string _contentType = "text/javascript"; 
            string _type = _request["t"] ?? string.Empty;
            string _path = _request["p"] ?? string.Empty;
            var  _debug = _request.IsLocal;
            if (!string.IsNullOrEmpty(_type) && _type == "css")
            {
                _contentType = "text/css";
            }

            string _version = _request["v"] ?? string.Empty;
            string _files = ConfigurationManager.AppSettings[_setName] ?? "";
            // Decide if browser supports compressed response
            bool _isCompressed =  CanGZip(context.Request);

            // Response is written as UTF8 encoding. If you are using languages like
            // Arabic, you should change this to proper encoding 
            //UTF8Encoding encoding = new UTF8Encoding(false);


            string eTag = "\"" + (GetCacheKey(_files, _version, _isCompressed)).GetHashCode() + "\"";

            if (!_debug && CheckETag(context, eTag))
            {
                return;
            }


            var _sb = new StringBuilder();

            // If the set has already been cached, write the response directly from
            // cache. Otherwise generate the response and cache it
           // if (!WriteFromCache(context, _setName, version, _isCompressed, _contentType))
            
               
                string[] _fileNames = _files.Split(new char[] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (string _fileName in _fileNames)
                {

                    string _filePath = !string.IsNullOrEmpty(_path)
                                           ? string.Format("{0}/{1}", _path, _fileName.Trim())
                                           : _fileName.Trim();

                    _sb.AppendLine("");
                    _sb.AppendFormat("/* {0} */", _fileName);
                    _sb.AppendLine("");

                    _sb.AppendLine(GetFileAsString(context, _filePath));

                }


                byte[] _bytes = UTF8Encoding.UTF8.GetBytes(_sb.ToString());

                using (var _memoryStream = new MemoryStream())
                {
                    using (Stream _writer = _isCompressed ? (Stream)(new GZipStream(_memoryStream, CompressionMode.Compress)) : _memoryStream)
                    {

                        _writer.Write(_bytes, 0, _bytes.Length);


                    }
                    byte[] _responseBytes = _memoryStream.ToArray();


                   // context.Cache.Insert(GetCacheKey(_files, version, _isCompressed), _responseBytes, null, System.Web.Caching.Cache.NoAbsoluteExpiration, CACHE_DURATION);

                    // Generate the response
                    WriteBytes(_responseBytes, context, _isCompressed, _contentType,eTag);
                }


                
            
        }


        private static string GetFileAsString(HttpContext _context, string _virtualPath)
        {

            if (_virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
            {
                using (var _client = new WebClient())
                {
                    return _client.DownloadString(_virtualPath);
                }
            }
            string _physicalPath = _context.Server.MapPath(_virtualPath);
            return File.Exists(_physicalPath) ? File.ReadAllText(_physicalPath) : string.Empty;
        }


        /*

        private static byte[] GetFileBytes(HttpContext context, string virtualPath, Encoding encoding)
        {
            if (virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
            {
                using (WebClient client = new WebClient())
                {
                    return client.DownloadData(virtualPath);
                }
            }
            else
            {
                string physicalPath = context.Server.MapPath(virtualPath);
                byte[] bytes = File.ReadAllBytes(physicalPath);
                // TODO: Convert unicode files to specified encoding. For now, assuming
                // files are either ASCII or UTF8
                return bytes;
            }
        }
         * */

        //private bool WriteFromCache(HttpContext context, string setName, string version,
        //    bool isCompressed, string contentType)
        //{
        //    if (!_RELEASE) return false;

        //    byte[] responseBytes = context.Cache[GetCacheKey(setName, version, isCompressed)] as byte[];

        //    if (null == responseBytes || 0 == responseBytes.Length) return false;

        //    WriteBytes(responseBytes, context, isCompressed, contentType);
        //    return true;
        //}

        /// <summary>
        /// Check if the ETag that sent from the client is match to the current ETag.
        /// If so, set the status code to 'Not Modified' and stop the response.
        /// </summary>
        private static bool CheckETag(HttpContext context, string eTagCode)
        {
            string ifNoneMatch = context.Request.Headers["If-None-Match"];
            if (eTagCode.Equals(ifNoneMatch, StringComparison.Ordinal))
            {
                context.Response.AppendHeader("Content-Length", "0");
                context.Response.StatusCode = (int)HttpStatusCode.NotModified;
                context.Response.StatusDescription = "Not modified";
                context.Response.SuppressContent = true;
                context.Response.Cache.SetCacheability(HttpCacheability.Public);
                context.Response.Cache.SetETag(eTagCode);
                context.Response.Cache.SetExpires(DateTime.Now.AddYears(1));
                context.Response.End();
                return true;
            }

            return false;
        }

            private static void WriteBytes(byte[] _bytes, HttpContext _context, bool _isCompressed, string _contentType, string eTag)
        {
            HttpResponse _response = _context.Response;

            _response.AppendHeader("Content-Length", _bytes.Length.ToString());
            _response.ContentType = _contentType;
            if (_isCompressed)
                _response.AppendHeader("Content-Encoding", "gzip");

            _context.Response.Cache.SetCacheability(HttpCacheability.Public);
            _context.Response.Cache.SetExpires(DateTime.Now.Add(CACHE_DURATION));
            _context.Response.Cache.SetMaxAge(CACHE_DURATION);
            _context.Response.Cache.SetETag(eTag);
            _context.Response.ContentType = _contentType;
            _context.Response.Cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
            _response.OutputStream.Write(_bytes, 0, _bytes.Length);
            _response.Flush();
        }

        /*
        private void WriteBytes(byte[] bytes, HttpContext context,
            bool isCompressed, string contentType)
        {
            HttpResponse response = context.Response;

            response.AppendHeader("Content-Length", bytes.Length.ToString());
            response.ContentType = contentType;
            if (isCompressed)
                response.AppendHeader("Content-Encoding", "gzip");

            context.Response.Cache.SetCacheability(HttpCacheability.Public);
            context.Response.Cache.SetExpires(DateTime.Now.Add(CACHE_DURATION));
            context.Response.Cache.SetMaxAge(CACHE_DURATION);
            context.Response.Cache.AppendCacheExtension("must-revalidate, proxy-revalidate");

            response.OutputStream.Write(bytes, 0, bytes.Length);
            response.Flush();
        }

         * */
        private bool CanGZip(HttpRequest request)
        {
            string acceptEncoding = request.Headers["Accept-Encoding"];
            if (!string.IsNullOrEmpty(acceptEncoding) &&
                 (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate")))
                return true;
            return false;
        }

        private string GetCacheKey(string _name, string _version, bool _isCompressed)
        {
            return string.Format("RESOURCE_COMPRESS.{0}.{1}.{2}", _name, _version, _isCompressed);
        }

        public bool IsReusable
        {
            get
            {
                return true;
            }
        }
    }
}
